#include "depui/depui.h"
#include <string.h>

#define MXMODULE_LIST

void mx_sysmenu_popup_handler(MX_WIN * win)
{
	MX_SYSMENU_POPUP *s = (MX_SYSMENU_POPUP *) win;

	MXDEBUG_INVARIANT(MXOBJ(s));

	/* Perform the menu callback */
	if ((mx.event == MX_SELECT) && (mx.data) && (MXCLASS(mx.obj) == mx_listelem_class)) {

		MX_SYSMENU_ELEM *elem = (MX_SYSMENU_ELEM *) mx.obj;

		if (elem->func)
			elem->func();

		assert(mx__sysmenu);
		mx_dirty(mx__sysmenu, true);
		mx_delete(s);
		return;

	} else if (mx_eventmatch(MX_KEY_UNUSED, s)) {
		const MX_KEY_INFO *info = mx_key_info();

		if ((info) && (info->ascii == 0x1b)) {
			assert(mx__sysmenu);
			mx_dirty(mx__sysmenu, true);
			mx_win_dirty(s);
			mx_delete(s);
		}

	} else if (mx_eventmatch(MX_DESTRUCT, s)) {
		assert(mx__sysmenu);
		assert(mx__sysmenu->_popup == s);
		mx_dirty(mx__sysmenu, true);
		mx__sysmenu->_popup = 0;

	} else if (mx_eventmatch(MX_THEME_CHANGE, s)) {
		mx_defaultrect(s, 0);
		mx_geometry(s);

	} else if (mx_eventmatch(MX_DEFAULTRECT, s)) {
		MX_RECT list;
		MX_RECT *rrect = mx_defaultrect_data();

		mx_defaultrect(&s->list, &list);

		assert(mx__sysmenu);
		rrect->x1 = mx_x2(mx__sysmenu);
		rrect->y1 = mx_y2(mx__sysmenu);
		rrect->x2 = rrect->x1 + (list.x2 - list.x1);
		rrect->y2 = rrect->y1 + (list.y2 - list.y1);

		list = *mx_platform_rect();

		MXRECT_INTERSECT(*rrect, list, *rrect);
		return;

	} else if (mx_eventmatch(MX_GEOMETRY, s)) {
		const MX_RECT *rrect = MXRECT(s);

		mx_place(&s->list, rrect);
		mx_geometry(&s->list);
	}

	mx_default_handler();
}

static void mx__sysmenu_popup(void)
{
	assert(mx__sysmenu);
	assert(mx__sysmenu->_popup == 0);
	mx__sysmenu->_popup = (MX_SYSMENU_POPUP *) mx__win(0, sizeof(MX_SYSMENU_POPUP), mx_sysmenu_popup_handler, 0);

	if (mx__sysmenu->_popup) {
		unsigned int i;
		mx_list(&mx__sysmenu->_popup->list, 0, mx__sysmenu->_popup, 0);

		/* Copy the menu entries in the main menu into our list */
		for (i = 0; i < mx_vector_size(&mx__sysmenu->_entries); i++) {
			MX_SYSMENU_ELEM *elem;
			MX_SYSMENU_ENTRY *entry = &mx__sysmenu->_entries.data[i];
			int len = 0;
			const char *text = mx_string_text(&entry->text, &len);

			elem = (MX_SYSMENU_ELEM *) mx__listelem(0, sizeof(MX_SYSMENU_ELEM), MXLIST(&mx__sysmenu->_popup->list), 0);

						/**fix Kind of dangerous to maintain pointers to the entry text
               of the main menu, maybe make a copy here? */
			mx_text_set(elem, text, len, 0);
			elem->func = entry->func;
		}

		mx_win_nomove(mx__sysmenu->_popup, true);
		mx_win_noclose(mx__sysmenu->_popup, true);
		mx_win_noresize(mx__sysmenu->_popup, true);
		mx_win_notitle(mx__sysmenu->_popup, true);

		mx_modal(mx__sysmenu->_popup);
		mx__popup(MXWIN(mx__sysmenu->_popup));

		mx_defaultrect(mx__sysmenu->_popup, 0);
		mx_geometry(mx__sysmenu->_popup);
		mx_win_dirty(mx__sysmenu->_popup);
	}
}

void mx_sysmenu_class(void)
{
	MX_SYSMENU *s = (MX_SYSMENU *) mx.obj;

	MXDEBUG_INVARIANT(MXOBJ(s));
	assert(s == mx__sysmenu);

	if (mx.event == MX_DESTRUCT) {
		unsigned i;

		if (s->_popup)
			mx_delete(s->_popup);

		for (i = 0; i < mx_vector_size(&s->_entries); i++)
			mx_string_free(&s->_entries.data[i].text);

		mx_vector_free(&s->_entries);

	} else if ((mx.event == MX_SELECT) && (mx.data) && (!s->_popup)) {
		mx__sysmenu_popup();
		return;

	} else if (mx.event == MX_THEME_CHANGE) {
		mx_button_class();

		mx_defaultrect(s, 0);
		mx_geometry(s);
		return;
	}

	mx_button_class();
}

/** !Create a system menu */
void mx_sysmenu(void)
{
	if (mx__sysmenu)
		return;

	mx__sysmenu = (MX_SYSMENU *) mx__button(0, sizeof(MX_SYSMENU), 0, 0);
	if (mx__sysmenu) {
		MXOBJ(mx__sysmenu)->_class = mx_sysmenu_class;
		MXDEBUG_ALLOCTAG(mx__sysmenu);
		MXDEBUG_ATOMNAME(mx__sysmenu, "sysmenu");
		mx_text_set(mx__sysmenu, "DEPUI", -1, 0);

		/* Put at least an exit entry */
		mx_sysmenu_add("exit", -1, 0, mx__puiexit);

		mx_move(mx__sysmenu, 0, 0);
		mx_defaultrect(mx__sysmenu, 0);
		mx_geometry(mx__sysmenu);
		mx_dirty(mx__sysmenu, true);
	}

	MXDEBUG_INVARIANT(MXOBJ(mx__sysmenu));
}

void mx_sysmenu_add(const char *text, int len, MX_FREE dfree, MX_SYSMENU_FUNC func)
{
	MX_SYSMENU_ENTRY entry;

	if (!mx__sysmenu)
		mx_sysmenu();
	assert(mx__sysmenu);

	memset(&entry, 0, sizeof(entry));
	mx_string(&entry.text);
	mx_string_set(&entry.text, text, len, dfree);
	entry.func = func;

	mx_vector_insert(&mx__sysmenu->_entries, &entry, 1, 0);
}
